package cn.ilikes.tools.nosql.jpa.persistences;

import java.util.Map;

import java.util.Properties;

import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import org.slf4j.Logger;

import cn.ilikes.tools.logs.Loggerfactory;
import cn.ilikes.tools.nosql.jpa.JPASQLPersistenUnitEnv;
import cn.ilikes.tools.nosql.jpa.clients.AbClient;
import cn.ilikes.tools.nosql.jpa.configs.JPASQLPersistenceUnitMetadata;
import cn.ilikes.tools.nosql.jpa.configs.querys.NamedQuery;
import cn.ilikes.tools.nosql.jpa.graphs.Node;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.AbstractContext;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.JPAHBaseTransaction;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.JPASQLDefaultSessionContext;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.Operation;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.operations.FindOperation;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.operations.PersistOperation;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.operations.ReferenceOperation;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.operations.RemoveOperation;
import cn.ilikes.tools.nosql.jpa.query.ABSQLQuery;
import cn.ilikes.tools.nosql.jpa.query.JPASQLQueryRunTimeException;
import cn.ilikes.tools.nosql.jpa.query.SQLQueryImpl;
import cn.ilikes.tools.nosql.jpa.query.HQueryImpl.SQLQueryType;
import cn.ilikes.tools.nosql.jpa.query.iquerys.JPAQuery;
import cn.ilikes.tools.nosql.jpa.query.iquerys.JPASQLQuery;
import cn.ilikes.tools.nosql.jpa.query.pasers.DefultJPASQLQueryParser;
import cn.ilikes.tools.nosql.jpa.query.pasers.IJPAQueryParser;
import cn.ilikes.tools.nosql.jpa.query.pasers.JPQLParserException;

public abstract class ABJPASQLEntityManager extends JPASQLEntityManagerBase implements IJPASQLEntityManager {

	private static Logger logger = Loggerfactory.instance(ABJPASQLEntityManager.class);

	private JPASQLPersistenUnitEnv persistenUnitEnv;

	private AbstractContext context;

	private Map<Object, Object> properties;

	private EntityTransaction entityTransaction;

	public ABJPASQLEntityManager(JPASQLPersistenUnitEnv persistenUnitEnv, AbClient clients, Map<Object, Object> properties) {
		this.persistenUnitEnv = persistenUnitEnv;
		this.properties = properties;
		AbstractContext context = new JPASQLDefaultSessionContext(persistenUnitEnv, clients);
		this.setContext(context);
	}

	public String getPuName() {
		return persistenUnitEnv.getPersistenUnitName();
	}

	private void setEntityTransaction(EntityTransaction entityTransaction) {
		this.entityTransaction = entityTransaction;
	}

	protected AbstractContext getContext() {
		return context;
	}

	protected void setContext(AbstractContext context) {
		this.context = context;
	}

	@Override
	public void clear() {
		this.context.clear();
	}

	@Override
	public void close() {
		this.context.close();
	}

	@Override
	public void flush() {

		super.flush();
	}

	@Override
	public EntityTransaction getTransaction() {
		EntityTransaction entityTransaction = this.entityTransaction;
		if (entityTransaction == null) {
			synchronized (this) {
				if (Loggerfactory.isdebug(logger))
					Loggerfactory.debug(logger, "create  entity transaction ==================");
				AbstractContext context = getContext();
				entityTransaction = new JPAHBaseTransaction(context);
				this.setEntityTransaction(entityTransaction);
			}
		}
		return entityTransaction;
	}

	@Override
	public boolean isOpen() {

		return super.isOpen();
	}

	public Object getDelegate() {
		return getSpecificImpls();
	}

	protected abstract Object getSpecificImpls();

	@Override
	public void persist(Object entity) {
		if (Loggerfactory.isdebug(logger))
			Loggerfactory.debug(logger, "persist : " + entity);
		AbstractContext abstractContext = getContext();
		Operation<Boolean> persist = new PersistOperation(entity, abstractContext);
		abstractContext.operation(persist);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T find(Class<T> arg0, Object key) {
		AbstractContext abstractContext = getContext();
		Operation<Node> persist = new FindOperation(abstractContext, arg0, key);
		Node node = (Node) abstractContext.operation(persist);
		if(node==null) return null;
		return (T) node.getEntity();
	}

	@Override
	public void remove(Object entity) {
		AbstractContext abstractContext = getContext();
		Operation<Boolean> persist = new RemoveOperation(abstractContext, entity);
		abstractContext.operation(persist);
	}

	@Override
	public Query createQuery(String arg0) {
		AbClient client = context.getClient();
		JPAQuery jpaQuery = new JPASQLQuery(client, context, arg0);
		try {
			IJPAQueryParser kunderaQueryParser = new DefultJPASQLQueryParser(jpaQuery, arg0);
			kunderaQueryParser.parser();
		} catch (JPQLParserException e) {
			throw new JPASQLQueryRunTimeException(e);
		}

		Query baseQuery = new SQLQueryImpl(jpaQuery, getContext(), SQLQueryType.General);
		return baseQuery;
	}

	@Override
	public Query createNamedQuery(String nameId) {
		JPASQLPersistenceUnitMetadata persistenceUnitMetadata = this.persistenUnitEnv.getPersistenceUnitMetadata();
		NamedQuery namedQuery = persistenceUnitMetadata.getEntityUntils().getNamedQuerys().get(nameId);
		String query = namedQuery.getQuery();
		if (Loggerfactory.isdebug(logger))
			Loggerfactory.debug(logger, "create Named Query " + nameId + "  " + query);
		if (query == null && query.isEmpty())
			throw new JPASQLQueryRunTimeException("not find " + nameId + " query  ");
		return createQuery(query);
	}

	@Override
	public <T> T getReference(Class<T> entity, Object key) {
		AbstractContext abstractContext = getContext();
		Operation<Node> persist = new ReferenceOperation(abstractContext, key, entity);
		Node node = (Node) abstractContext.operation(persist);
		return (T) node.getEntity();
	}

	@Override
	public Query createPagesQuery(String sql, Properties properties) {
		AbClient client = context.getClient();
		JPAQuery jpaQuery = new JPASQLQuery(client, context, sql);
		try {
			IJPAQueryParser kunderaQueryParser = new DefultJPASQLQueryParser(jpaQuery, sql);
			kunderaQueryParser.parser();
		} catch (JPQLParserException e) {
			throw new JPASQLQueryRunTimeException(e);
		}
		ABSQLQuery baseQuery = new SQLQueryImpl(jpaQuery, getContext(), SQLQueryType.Pages);
		baseQuery.setProperties(properties);
		return baseQuery;
	}
}
